#pragma once
#include "../World/Block.h"
#include "../Global References/Globals.h"

struct RetVal {
	bool x,y;
	void *xObj,*yObj;
	int xP,yP;
	inline void Add(RetVal const & R){
		if(!x && R.x) x = true;
		if(!y && R.y) y = true;
		if(!xObj && R.xObj) xObj = R.xObj;
		if(!yObj && R.yObj) yObj = R.yObj;
		xP = R.xP;
		yP = R.yP;
	}
	inline void clear() {
		x = y = 0;
		xObj = yObj = NULL;
		xP = -1;
		yP = -1;
	}
	RetVal():x(0),y(0),xObj(NULL),yObj(NULL),xP(-1),yP(-1){}
	~RetVal() {xObj = yObj = NULL;}
};

struct AssetIDLink {
	Block Type;
	unsigned int ID;

	AssetIDLink() : Type(0),
		ID(0)
	{}

	AssetIDLink(Block a_Type,unsigned int a_ID) : Type(a_Type),
		ID(a_ID)
	{}

	AssetIDLink(Block * a_Type,unsigned int a_ID) : Type(*a_Type),
		ID(a_ID)
	{}
	void operator= (AssetIDLink rhs) {
		this->Type = rhs.Type;
		this->ID = rhs.ID;
	}
	void operator= (AssetIDLink * rhs) {
		this->Type = rhs->Type;
		this->ID = rhs->ID;
	}
};

struct Block_Info {
	Block * Data;
	int X;
	int Y;

	Block_Info() : Data(NULL),
		X(0),
		Y(0)
	{
		Data = new Block;
	}
	
	Block_Info(Block a_Data,int a_X,int a_Y) : Data(&a_Data),
		X(a_X),
		Y(a_Y)
	{}

	Block_Info(Block * a_Data,int a_X,int a_Y) : Data(a_Data),
		X(a_X),
		Y(a_Y)
	{}

	void clear() {
		Data = NULL;
		X = 0;
		Y = 0;
	}
};

struct Chunk_Border {
	Block Border[ CHUNK_HEIGHT * 2 + CHUNK_WIDTH * 2 - 4 ];
	bool Set;
	Chunk_Border() : Set(false)
	{}
	~Chunk_Border() {
		CleanUp();
	}
	
	void Setup() {
	}

	void CleanUp() {
	}

	void ResetData(Block * C) {
		Set = false;
		SetData(C);
	}
	void SetData(Block * C) {
		if(!Set) {
			Set = true;
			int y2 = 0;
			int xAdd = 0;
			int yArMod = 0;
			for(int y = 0;y < CHUNK_HEIGHT;y++) {
				
				//Used to itterate properly through the border
				// Ensures no negative values
				if( y > 0 ) { y2 = y - 1; }

				//Used when setting the left and right borders, jumps
				// from one side to the other.
				if( xAdd != CHUNK_WIDTH ) xAdd = CHUNK_WIDTH-1;
				
				//Makes sure that the data in the first row is skipped over
				if( !yArMod && y == 1 ) { yArMod = CHUNK_HEIGHT; }

				//Makes the chunk move through the top and bottom borders one by one
				if( (y == 0 || y == CHUNK_HEIGHT-1) && xAdd != 1 )
				{ xAdd = 1; }
			
				for(int x = 0,ax = 0;x < CHUNK_WIDTH;x+=xAdd,ax++) {
					if((ax + ((y2 * 2) + yArMod)) < 0 || (ax + ((y2 * 2) + yArMod)) >= 196 )
						break;
					Border[ax + ((y2 * 2) + yArMod)] = C[x + (y * CHUNK_HEIGHT)];
				}
			}
		}
	}
	Block * Get(int const & x,int const & y) {
		if ( y == 0 || y == CHUNK_HEIGHT-1 ) {
			if( x >= 0 && x < CHUNK_WIDTH ) {
				if ( y == 0 ) {
					return &Border[x];
				}
				if((x + (((y-1) * 2) + CHUNK_WIDTH)) >= 0 && (x + (((y-1) * 2) + CHUNK_WIDTH)) <= 195)
					return &Border[x + (((y-1) * 2) + CHUNK_WIDTH)];
				else
					return &Border[0];
			}
		}

		if ( x == 0 || x == CHUNK_WIDTH-1) {
			if( y > 0 && y < CHUNK_HEIGHT-1 ) {
				if( x == CHUNK_WIDTH-1 ) {
					if((x + (((y-1) * 2) + CHUNK_WIDTH)) >= 0 && (x + (((y-1) * 2) + CHUNK_WIDTH)) <= 195)
						return &Border[1 + (((y-1) * 2) + CHUNK_WIDTH)];
					else
						return &Border[0];
				}
				if((x + (((y-1) * 2) + CHUNK_WIDTH)) >= 0 && (x + (((y-1) * 2) + CHUNK_WIDTH)) <= 195)
					return &Border[0 + (((y-1) * 2) + CHUNK_WIDTH)];
				else
					return &Border[0];
			}
		}
		return NULL;
	}
	// 'l' = left 'r' = right
	Block_Info * FindMatchInCol(char const & Col,unsigned int const & a_Dat) {
		int xCol;
		switch(Col) {
		case 'l':case 'L': xCol = 0; break;
		case 'r':case 'R': xCol = 49; break;
		default: xCol = -1;
		}
		if(xCol >= 0) {
			Block * ptr;
			for(int y = 0;y < CHUNK_HEIGHT;y++) {
				ptr = Get(xCol,y);
				if(ptr && ptr->GetData_s() == a_Dat) {
					return new Block_Info(ptr,xCol,y);
				}
			}
		}
		else 
			return NULL;
		return NULL;
	}
	Block_Info * FindSolidInCol(char const & Col) {
		int xCol;
		switch(Col) {
		case 'l':case 'L': xCol = 0; break;
		case 'r':case 'R': xCol = 49; break;
		default: xCol = -1;
		}
		if(xCol >= 0) {
			Block * ptr;
			for(int y = 0;y < CHUNK_HEIGHT;y++) {
				ptr = Get(xCol,y);
				if(ptr && ptr->GetType_s() > BLOCK_SOLIDS) {
					return new Block_Info(ptr,xCol,y);
				}
			}
		}
		else 
			return NULL;
		return NULL;
	}
};

struct CHUNK_INFO {
	Chunk_Border B;
	short X,Y;
	CHUNK_INFO():X(0),Y(0){
	}
	~CHUNK_INFO() {
		X = 0;
		Y = 0;
	}
	void Setup() {
		B.Setup();
	}
	void CleanUp() {
		B.CleanUp();
	}
	void Reset(short const & a_cX,short const & a_cY,Block * C) {
		X = a_cX;
		Y = a_cY;
		if(!B.Border) 
		{
			B.SetData(C);
		}
		else 
			B.ResetData(C);
	}
	void Set(short const & a_cX,short const & a_cY,Block * C) {
		X = a_cX;
		Y = a_cY;
		B.SetData(C);
	}
};

struct SpecialGen {
	//Which chunk needs a special execution of generation
	CHUNK_INFO * pC;
	//Any extra information it needs to know to generate later
	void * Data;
	//If this chunk has executed the special rules yet
	bool Completed;

	SpecialGen() : Data(NULL) , Completed(false) , pC(NULL) {}
	SpecialGen(CHUNK_INFO * C) : Data(NULL) , pC(C) , Completed(false) {}
	SpecialGen(void * a_D,CHUNK_INFO * C) : Data(a_D) , Completed(false) , pC(C) {}
};

struct Tool1SpecialData {
	// 1 = Up
	// 2 = Down
	char Dir;

	//Block Information
	unsigned int BDat;

	//Amount of blocks attempting to add
	short Movement;
};